#include "QxWellLasFile.hpp"

#include <QStringList>
#include <QTextStream>

class QxWellLasFile::QxWellLasFilePrivate
{
public:

  QxWellLasFilePrivate( QxWellLasFile* me ) : m_self( me ) {
  }

  void loadWellInfo() {
    QFile hFile( m_self->filename() );
    if( !hFile.open ( QFile::ReadOnly ) ) {
      return;
    }

    int tildeCnt = 0;
    QTextStream dataIn( &hFile );
    while( tildeCnt < 2 ) {
      dataIn.readLine().contains( "~" ) && (++tildeCnt);
    }
    extractWellInfo( dataIn );
    extractWellLogDefines( dataIn );
    hFile.close();
  }

  bool loadWellData() {
    if( m_self->wellLogDefines().isEmpty() ) {
      return false;
    }

    QFile hFile( m_self->filename() );
    if( !hFile.open ( QFile::ReadOnly ) ) {
      return false;
    }

    int tildeCnt = 0;
    QTextStream dataIn( &hFile );
    while( tildeCnt < 4 ) {
      dataIn.readLine().contains( "~" ) && (++tildeCnt);
    }

    extractDatas( dataIn );
    hFile.close();
    return true;
  }

  void extractWellInfo( QTextStream& dataIn ) {
    QMap<QString, QVariant> infos;
    while( !dataIn.atEnd() ) {
      QString str = dataIn.readLine();
      if( str.contains( "~" ) ) {
        break;
      }

      QStringList seg = str.split( ":" );
      if( seg.count() > 1 ) {
        seg = seg.first().trimmed().split( QRegExp( "\\s+" ) );
        if( seg.count() > 1 ) {
          infos[seg.first().trimmed()] = seg.at( 1 ).trimmed();
        }
      }
    }

    QxWellInfoDefine wellInfoDefine;
    wellInfoDefine.wellName = infos[ "WELL." ].toString();
    wellInfoDefine.location = QPointF( infos["Xzuobiao."].toFloat(), infos["Yzuobiao."].toFloat() );
    wellInfoDefine.startDepth = infos["STRT.M"].toFloat();
    wellInfoDefine.stopDepth = infos["STOP.M"].toFloat();
    wellInfoDefine.stepDepth = infos["STEP.M"].toFloat();
    wellInfoDefine.nullValue = infos["NULL."].toFloat();
    m_self->setWellInfoDefine( wellInfoDefine );
  }

  void extractWellLogDefines( QTextStream& dataIn ) {
    QStringList wellLogNameList;
    while( !dataIn.atEnd() ) {
      QString str = dataIn.readLine();
      if( str.contains( "~" ) ) {
        QStringList seg = str.trimmed().split( QRegExp( "\\s+" ) );
        seg.pop_front();
        if( seg.count() != wellLogNameList.count() ) {
          wellLogNameList.clear();
        } else {
          wellLogNameList = seg;
        }
        break;
      }

      QStringList seg = str.split( ":" );
      if( seg.count() > 1 ) {
        seg = seg.first().trimmed().split( "." );
        if( seg.count() > 1 ) {
          wellLogNameList << seg.first();
        }
      }
    }

    QxWellInfoDefine wellInfoDefine = m_self->wellInfoDefine();
    Q_FOREACH( const QString& wellLogName, wellLogNameList ) {
      QxWellLogDefine wellLogDefine;
      wellLogDefine.wellName = wellInfoDefine.wellName;
      wellLogDefine.wellLogName = wellLogName;
      wellLogDefine.nullValue = wellInfoDefine.nullValue;
      m_self->insertWellLogDefine( wellLogDefine );
    }
  }

  void extractDatas( QTextStream& dataIn ) {
    QVector<QxWellLogDefine> wellLogDefines = m_self->wellLogDefines();
    QVector< QVector<float> > datas( wellLogDefines.count() );
    while( !dataIn.atEnd() ) {
      QString str = dataIn.readLine();
      if( str.isEmpty() ) {
        break;
      }

      if( str.count() != datas.count()*11 ) {
        continue;
      }

      for( int idx = 0; idx < datas.count(); ++idx ) {
        datas[idx] << str.mid( idx*11, 11 ).toFloat();
      }
    }

    for( int idx = 0; idx < datas.count(); ++idx ) {
      int pos = 0;
      int length = 0;
      updateWellLogDefine( wellLogDefines[idx], datas.first(), datas.at( idx ), pos, length );
      m_dataMapper[wellLogDefines.at( idx ).wellLogName] = datas.at( idx ).mid( pos, length );
    }

    m_self->clear();
    Q_FOREACH( const QxWellLogDefine& wellLogDefine, wellLogDefines ) {
      m_self->insertWellLogDefine( wellLogDefine );
      m_self->setData( wellLogDefine.wellLogName, m_dataMapper[wellLogDefine.wellLogName] );
    }
  }

  void updateWellLogDefine( QxWellLogDefine& define,
                            const QVector<float>& depth,
                            const QVector<float>& data,
                            int& pos,
                            int& length ) {
    int idx0 = 0;
    int idx1 = data.count()-1;
    QxWellInfoDefine wellInfoDefine = m_self->wellInfoDefine();
    while( (idx0 < data.count()) && (data.at(idx0)==wellInfoDefine.nullValue) && (++idx0) );
    while( (idx1 >= 0) && (data.at(idx1)==wellInfoDefine.nullValue) && (idx1--) );
    if( idx0 <= idx1 ) {
      define.startDepth = depth.at( idx0 );
      define.stopDepth  = depth.at( idx1 );
      define.stepDepth  = wellInfoDefine.stepDepth;
    }
    pos = idx0;
    length = idx1-idx0+1;
  }

  void clear() {
    m_dataMapper.clear();
    m_self->clear();
  }

  QxWellLasFile*                 m_self;
  QMap<QString, QVector<float> > m_dataMapper;
};

QxWellLasFile::QxWellLasFile() : _pd( new QxWellLasFilePrivate( this ) )
{
}

QxWellLasFile::~QxWellLasFile()
{
}

QVector<float> QxWellLasFile::data( const QString& wellLogName ) const
{
  QVector<float> result;
  _pd->m_dataMapper.isEmpty() && _pd->loadWellData();
  if( _pd->m_dataMapper.contains( wellLogName ) ) {
    result = _pd->m_dataMapper[wellLogName];
  }

  return result;
}

void QxWellLasFile::setData( const QString& wellLogName, const QVector<float>& data )
{
  _pd->m_dataMapper[wellLogName] = data;
}

bool QxWellLasFile::prepare()
{
  return _pd->loadWellData();
}

void QxWellLasFile::reload()
{
  _pd->clear();
  _pd->loadWellInfo();
}
